import {PagesOptions, PagesOptionsPage, PagesSubPackages} from "./types";
import {ConfigLoader, PagesLoader} from "./loader";
import {PathUtil} from "./utils/PathUtil";
import {PageParser} from "./utils/PageParser";
import {TypesGenerator} from "./utils/TypesGenerator";
import path from "path";
import fs from "fs";
import * as micromatch from 'micromatch';
import type {ViteDevServer} from 'vite';
import {Logger, normalizePath} from 'vite';
import {PagesJsonHelper} from "./helper";
import {FSWatcher} from "chokidar";

export class PagesContext {

    /**
     * 配置加载器
     */
    private readonly configLoader: ConfigLoader;

    private readonly pagesLoader: PagesLoader;

    /**
     * Vite 开发服务器实例
     */
    private _server: ViteDevServer | undefined;

    /**
     * Logger 实例
     */
    private readonly logger: Logger;

    constructor(configLoader: ConfigLoader, logger: Logger) {
        // 初始化配置加载器
        this.configLoader = configLoader;
        this.logger = logger;
        // 初始化页面加载器
        this.pagesLoader = new PagesLoader(this.configLoader);
        // 扫描页面
        this.pagesLoader.scanPages('main');
        this.pagesLoader.scanPages('sub');
    }

    /**
     * 生成 pages.json 内容
     * @returns 生成的 pages.json 对象
     */
    private async generatePagesJson(): Promise<PagesOptions> {
        const {pages, subPackages: subPackagesConfig, ...otherConfigs} = this.configLoader.pages;

        // 1. 获取扫描到的主包页面（已包含配置文件中的页面）
        const allMainPages = Array.from(this.pagesLoader.main.values());

        // 2. 处理分包配置
        const subPackages = this.processSubPackages();

        // 3. 确定主页面并排序
        // 使用配置的主页面路径（优先级最高）
        const mainPagePath = this.configLoader.plugin.mainPage;

        // 查找配置的主页面（mainPage 配置优先级最高）
        const configuredMainPage = allMainPages.find(page => page.path === mainPagePath);
        if (!configuredMainPage) {
            this.logger.warn(`首页 ${mainPagePath} 不存在，请检查配置`);
        }
        const otherPages = !configuredMainPage ? allMainPages : allMainPages.filter(page => page.path !== configuredMainPage?.path);

        // 4. 合并配置生成最终的 pages.json
        const pagesJson: PagesOptions = {
            pages: configuredMainPage ? [configuredMainPage, ...otherPages] : allMainPages,
            subPackages: subPackages,
            ...(otherConfigs || {}),
        };

        return pagesJson;
    }

    /**
     * 处理分包配置
     * @returns 分包配置列表
     */
    private processSubPackages(): PagesSubPackages[] {
        // 如果没有分包配置返回空数组
        if (!this.configLoader.pages.subPackages || this.configLoader.pages.subPackages.length === 0) {
            return [];
        }

        const result: PagesSubPackages[] = [];

        for (let subPackage of this.configLoader.pages.subPackages) {
            // 获取该分包的所有页面（已包含配置文件中的页面）
            const subPackagePages: PagesOptionsPage[] = [];

            // 遍历扫描到的分包页面，找到属于当前分包的页面
            for (let [fullPath, pageConfig] of this.pagesLoader.sub) {
                if (fullPath.startsWith(subPackage.root + '/')) {
                    subPackagePages.push(pageConfig);
                }
            }

            result.push({
                root: subPackage.root,
                pages: subPackagePages
            });
        }

        return result;
    }

    /**
     * 生成 pages.json 文件
     * @returns 生成的 pages.json 内容
     */
    public async generate() {
        const json = await this.generatePagesJson();
        PagesJsonHelper.write(this.configLoader.plugin.pagesJsonPath, json);

        // 生成类型定义文件（如果启用）
        if (this.configLoader.plugin.generateTypes) {
            const typesOutputPath = path.resolve(this.configLoader.plugin.root, this.configLoader.plugin.typesOutputPath);
            TypesGenerator.generateTypes(json, typesOutputPath, this.logger);
        }
    }


    /**
     * 重新扫描页面
     * @param type 扫描类型
     * @param paths 页面路径，为空时扫描所有页面，否则按需扫描
     */
    public rescan(type?: 'main' | 'sub', paths?: string[]) {
        if (type) {
            this.pagesLoader.scanPages(type, paths);
        } else {
            // 扫描所有类型
            this.pagesLoader.scanPages('main', paths);
            this.pagesLoader.scanPages('sub', paths);
        }
    }

    /**
     * 检查文件是否是页面文件
     * @param fileName 文件名
     * @returns 是否是页面文件
     */
    isPageFile(fileName: string): boolean {
        const pageFileExts = this.configLoader.plugin.fileTypes;
        return pageFileExts.some(ext => fileName.endsWith(`.${ext}`));
    }

    /**
     * 检查文件是否为需要处理的页面文件（来自 vite handleHotUpdate）
     * @param file 文件绝对路径
     * @returns 是否是需要处理的页面文件
     */
    checkPage(file: string): boolean {
        // 1. 检查文件是否存在
        if (!fs.existsSync(file)) {
            return false;
        }

        // 2. 检查文件扩展名是否是页面文件
        if (!this.isPageFile(file)) {
            return false;
        }

        // 3. 获取相对于项目根目录的路径
        const relativePath = path.relative(this.configLoader.plugin.root, file);

        // 4. 检查文件是否在入口目录下
        const entryDir = this.configLoader.plugin.entryDir;
        const isInEntryDir = PathUtil.startsWith(relativePath, entryDir);

        // 如果不在入口目录下，不需要处理
        if (!isInEntryDir) {
            return false;
        }

        // 5. 检查文件是否被忽略
        const ignorePatterns = this.configLoader.plugin.ignore;
        if (ignorePatterns && ignorePatterns.length > 0) {
            const isIgnored = micromatch.isMatch(relativePath, ignorePatterns);
            if (isIgnored) {
                return false;
            }
        }

        // 6. 检查文件是否在页面缓存中或分包缓存中
        const pagePathFromFile = PageParser.convertToPagePath(relativePath);
        const isInMainPages = this.pagesLoader.main.has(pagePathFromFile);

        // 检查是否在分包中
        let isInSubPages = false;
        if (this.configLoader.pages.subPackages) {
            for (let subPackage of this.configLoader.pages.subPackages) {
                const subPageKey = `${subPackage.root}/${pagePathFromFile.startsWith(subPackage.root + '/')
                    ? pagePathFromFile.substring(subPackage.root.length + 1)
                    : pagePathFromFile}`;
                if (this.pagesLoader.sub.has(subPageKey)) {
                    isInSubPages = true;
                    break;
                }
            }
        }

        // 如果文件是页面文件且满足所有条件，则需要处理
        return isInMainPages || isInSubPages || true; // 即使不在缓存中，只要满足条件也应该处理
    }

    /**
     * 设置 Vite 开发服务器并启动文件监听
     * @param server Vite 开发服务器实例
     */
    setupViteServer(server: ViteDevServer) {
        if (this._server === server) {
            return;
        }

        this._server = server;
        this.setupWatcher(server.watcher);
    }

    /**
     * 设置文件监听器
     * @param watcher 文件监听器实例
     */
    setupWatcher(watcher: FSWatcher) {
        // 添加配置文件到监听列表
        const configPaths = this.configLoader.getConfigPaths();
        if (configPaths.length > 0) {
            // 注意：watcher.add需要的是原始路径，但configPaths已经被标准化了
            // 需要反向转换为系统路径格式供chokidar使用
            const systemConfigPaths = configPaths.map(p => path.resolve(p));
            watcher.add(systemConfigPaths);
        }

        // 监听文件添加
        watcher.on('add', async (filePath: string) => {
            filePath = normalizePath(filePath);

            if (!this.isPageFile(filePath)) {
                return;
            }

            if (!this.checkPage(filePath)) {
                return;
            }

            this.logger.info(`页面文件已添加: ${filePath}`);
            await this.handleFileChange(filePath);
        });

        // 监听文件修改
        watcher.on('change', async (filePath: string) => {
            filePath = normalizePath(filePath);

            // 检查是否是配置文件变更
            if (configPaths.includes(filePath)) {
                this.logger.info(`配置文件已修改: ${filePath}`);
                await this.handleConfigChange();
                return;
            }

            if (!this.isPageFile(filePath)) {
                return;
            }

            if (!this.checkPage(filePath)) {
                return;
            }

            this.logger.info(`页面文件已修改: ${filePath}`);
            await this.handleFileChange(filePath);
        });

        // 监听文件删除
        watcher.on('unlink', async (filePath: string) => {
            filePath = normalizePath(filePath);

            if (!this.isPageFile(filePath)) {
                return;
            }

            if (!this.checkPage(filePath)) {
                return;
            }

            this.logger.info(`页面文件已删除: ${filePath}`);
            await this.handleFileChange(filePath);
        });
    }

    /**
     * 处理文件变更
     * @param filePath 变更的文件路径
     */
    private async handleFileChange(filePath: string) {
        try {
            // 将绝对路径转为相对路径
            const relativePath = path.relative(this.configLoader.plugin.root, filePath);
            const pagePathFromFile = PageParser.convertToPagePath(relativePath);

            // 判断是主包还是分包页面，并按需扫描
            let isSubPackage = false;
            if (this.configLoader.pages.subPackages) {
                for (let subPackage of this.configLoader.pages.subPackages) {
                    if (pagePathFromFile.startsWith(subPackage.root + '/')) {
                        isSubPackage = true;
                        break;
                    }
                }
            }

            if (isSubPackage) {
                // 分包页面，只扫描分包
                this.rescan('sub', [relativePath]);
            } else {
                // 主包页面，只扫描主包
                this.rescan('main', [relativePath]);
            }

            // 重新生成 pages.json
            await this.generate();
            // 触发热更新
            this.onUpdate();
        } catch (error) {
            this.logger.error(`文件变更处理失败: ${error}`);
        }
    }

    /**
     * 处理配置文件变更
     */
    private async handleConfigChange() {
        try {
            // 重新加载配置
            await this.configLoader.loadPagesConfig();

            // 重新扫描页面
            this.rescan();
            // 重新生成 pages.json
            await this.generate();
            // 触发热更新
            this.onUpdate();
        } catch (error) {
            this.logger.error(`配置变更处理失败: ${error}`);
        }
    }

    /**
     * 触发热更新
     */
    private onUpdate() {
        if (!this._server) {
            return;
        }

        this._server.ws.send({
            type: 'full-reload',
        });
    }


}
